package com.ybear.ybcomponent.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import com.ybear.ybcomponent.OnPageDirectionChangedListener;
import com.ybear.ybcomponent.base.adapter.OnViewPagerAdapterListener;
import com.ybear.ybcomponent.base.adapter.pager.FragmentPagerAdapter;
import com.ybear.ybcomponent.base.adapter.pager.OnVisibleChangedListener;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Fragment切换页面控件
 */
public class FragmentViewPager extends ViewPager {
    private final List<OnPageDirectionChangedListener> mOnPageDirectionList = new ArrayList<>();
    private final List<Fragment> mFragments = new ArrayList<>();
    private FragmentActivity mFragmentActivity;
    private FragmentPagerAdapter mAdapter;
    private OnViewPagerAdapterListener mOnViewPagerAdapterListener;

    private boolean isEnableScroll = true;
    private boolean isEnableVisibleChanged = false;
    private int mReFragmentVisibleChangedCount = 0;
    private int mOldPosition = -1;
    private float mOldOffset = -1;

    public FragmentViewPager(@NonNull Context context) {
        this(context, null);
    }

    public FragmentViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        addOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float offset, int offsetPixels) {
                if( mOldOffset == -1 ) mOldOffset = offset;
                if( offset > 0 ) {
                    if( mOldOffset < offset ) {
                        //Right
                        onPageDirection( position, offset, offsetPixels, 1 );
                    }else if( mOldOffset > offset ) {
                        //Lift
                        onPageDirection( position, offset, offsetPixels, 0 );
                    }
                }
                mOldOffset = offset;
            }
            @Override
            public void onPageSelected(int position) {
//                if( mOldPosition == -1 ) mOldPosition = position;
                doOldPosition( 0 );
                if( mOldPosition <= position ) {
                    //Right
                    onPageDirection( position, mOldPosition, 1 );
                }else {
                    //Lift
                    onPageDirection( position, mOldPosition, 0 );
                }
                mOldPosition = position;
            }
            @Override
            public void onPageScrollStateChanged(int state) {
                for( OnPageDirectionChangedListener l : mOnPageDirectionList ) {
                    if( l != null ) l.onPageDirectionChanged( state );
                }
            }
        });
    }

    @Override
    public void setCurrentItem(int item) {
        super.setCurrentItem(item);
        doOldPosition( getCurrentItem() );
    }

    @Override
    public void setCurrentItem(int item, boolean smoothScroll) {
        super.setCurrentItem(item, smoothScroll);
        doOldPosition( getCurrentItem() );
    }

    private void doOldPosition(int position) {
        if( mOldPosition == -1 ) mOldPosition = position;
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent ev) { return isEnableScroll && super.onTouchEvent(ev); }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return isEnableScroll && super.onInterceptTouchEvent(ev);
    }

    public void setOnViewPagerAdapterListener(OnViewPagerAdapterListener l) {
        mOnViewPagerAdapterListener = l;
        if( mAdapter != null ) mAdapter.setOnViewPagerAdapterListener( mOnViewPagerAdapterListener );
    }

    public FragmentViewPager setFragmentActivity(FragmentActivity fa) {
        mFragmentActivity = fa;
        return this;
    }

    public boolean setFragments(@NonNull List<Fragment> f) {
        clearFragment();
        return mFragments.addAll( f );
    }

    @SafeVarargs
    public final <F extends Fragment> boolean setFragments(@NonNull F... f) {
        return setFragments( Arrays.asList( f ) );
    }

    public <F extends Fragment> boolean addFragment(@NonNull F f) {
        return mFragments.add( f );
    }

    public <F extends Fragment> FragmentViewPager addFragment(int index, @NonNull F f) {
        mFragments.add( index, f );
        return this;
    }

    public <F extends Fragment> boolean addFragmentAll(@NonNull List<F> list) {
        return mFragments.addAll( list );
    }

    public <F extends Fragment> boolean addFragmentAll(int index, @NonNull List<F> list) {
        return mFragments.addAll( index, list );
    }

    public <F extends Fragment> boolean removeFragment(@NonNull F f) {
        boolean ret = mFragments.remove( f );
        if( mFragments.size() == 0 ) reset();
        return ret;
    }

    public Fragment removeFragment(int index) {
        Fragment f = mFragments.remove( index );
        if( mFragments.size() == 0 ) reset();
        return f;
    }

    public FragmentViewPager clearFragment() {
        mFragments.clear();
        reset();
        return this;
    }

    /**
     * 获取数据源适配器
     * @return  适配器
     */
    public FragmentPagerAdapter getAdapter() {
        return mAdapter;
    }

    @Deprecated
    @Override
    public void setAdapter(@Nullable PagerAdapter adapter) {
//        super.setAdapter(adapter);
    }

    public void setAdapter(@Nullable FragmentPagerAdapter adapter) {
        mAdapter = adapter;
        notifyAdapter();
    }

    /**
     * 设置完毕后进行创建适配器等操作
     */
    public void notifyAdapter() {
        if( mAdapter == null ) {
            mAdapter = new FragmentPagerAdapter(
                    mFragmentActivity.getSupportFragmentManager(), mFragments
            );
            super.setAdapter( mAdapter );
        }
        mAdapter.setOnViewPagerAdapterListener( mOnViewPagerAdapterListener );
        mAdapter.notifyDataSetChanged();

        onVisibleChanged( getCurrentItem(), getCurrentItem() );
    }

    /**
     * 是否启用滑动
     * @param enable    是否启用
     * @return          this
     */
    public FragmentViewPager setEnableScroll(boolean enable) {
        isEnableScroll = enable;
        return this;
    }
    public boolean isEnableScroll() { return isEnableScroll; }

    /**
     * 启用/禁用Fragment可见/不可见时通知
     * 启用后需要在Fragment中实现 {@link OnVisibleChangedListener}
     * @param enable    是否启用
     */
    public FragmentViewPager setEnableVisibleChanged(boolean enable) {
        isEnableVisibleChanged = enable;
        return this;
    }
    public boolean isEnableVisibleChanged() { return isEnableVisibleChanged; }

    /**
     * 获取所有Fragment列表
     * @return  列表
     */
    @NonNull
    public List<Fragment> getFragmentList() { return new ArrayList<>( mFragments ); }

    /**
     * 获取指定位置的Fragment
     * @param position  下标
     * @return          Fragment
     */
    public Fragment getFragment(int position) {
        return checkPosition( position ) ? mFragments.get( position ) : null;
    }

    /**
     * 获取Fragment数量
     * @return  数量
     */
    public int getFragmentCount() { return mFragments.size(); }

    /**
     * 添加滑动方向改变事件监听器
     * @param l 监听器
     */
    public void addOnPageDirectionChangedListener(OnPageDirectionChangedListener l) {
        mOnPageDirectionList.add( l );
    }

    /**
     * 移除滑动方向改变事件监听器
     * @param l 监听器
     */
    public void removeOnPageDirectionChangedListener(OnPageDirectionChangedListener l) {
        mOnPageDirectionList.remove( l );
    }

    /**
     * 滑动方向改变
     * @param position                  下标
     * @param positionOffset            滑动小数级偏移
     * @param positionOffsetPixels      滑动像素级偏移
     * @param direction                 滑动方向
     */
    private void onPageDirection(int position, float positionOffset, int positionOffsetPixels,
                                 int direction) {
        for( OnPageDirectionChangedListener l : mOnPageDirectionList ) {
            if( l == null ) return;
            l.onPageDirection( position, positionOffset, positionOffsetPixels, direction );
        }
    }
    private void onPageDirection(int position, int oldPosition, int direction) {
        //隐藏事件改变
        onVisibleChanged( position, oldPosition );
        //通知页面位置发生改变
        for( OnPageDirectionChangedListener l : mOnPageDirectionList ) {
            if( l != null ) l.onPageDirection( position, oldPosition, direction );
        }
    }

    /**
     * 页面可见/不可见发生改变
     * @param position          下标
     * @param oldPosition       之前的下标
     */
    private void onVisibleChanged(int position, int oldPosition) {
        if( !isEnableVisibleChanged() ) return;
        Fragment f;

        //当前页面
        f = getFragment( position );
        if( checkPosition( position ) && f instanceof OnVisibleChangedListener ) {
            doFragmentVisibleChangedListener( f, position, true );
        }

        //相同页面时跳过
        if( position == oldPosition ) return;

        //上一个页面
        f = getFragment( oldPosition );
        if( checkPosition( oldPosition ) && f instanceof OnVisibleChangedListener ) {
            doFragmentVisibleChangedListener( f, oldPosition, false );
        }
    }

    private void doFragmentVisibleChangedListener(Fragment f, int pos, boolean isShow) {
        Context context = getContext();
        OnVisibleChangedListener l;
        if( mReFragmentVisibleChangedCount++ >= 5 ) {
            mReFragmentVisibleChangedCount = 0;
            return;
        }
        if( context == null ) {
            postDelayed(() -> doFragmentVisibleChangedListener( f, pos, isShow ), 250 );
            return;
        }

        l = ( (OnVisibleChangedListener)f );
        if( isShow ) {
            l.onFragmentShow( context, pos );
        }else {
            l.onFragmentHidden( context, pos );
        }
        mReFragmentVisibleChangedCount = 0;
    }

    private void reset() {
        mOldOffset = -1;
        mOldPosition = -1;
    }

    private boolean checkPosition(int position) {
        return position >= 0 && position < mFragments.size();
    }
}
